<template>
  <view class="js-calendar">
    <!-- 头部上下月按钮及月份 -->
    <view class="header">
      <view class="head-icon head-pre-month" v-show="showChangeBtn" @click="changeMonthOrWeek('prev')"></view>
      <view class="head-month">{{selDate.year+'年'+(selDate.month<10?'0'+selDate.month:selDate.month)+'月'}}</view>
      <view class="head-icon head-next-month" v-show="showChangeBtn" @click="changeMonthOrWeek('next')"></view>
      <view class="go-to-today" v-show="showToday" @click="goToday">今</view>
    </view>
    <!-- 星期 -->
    <view class="week-area">
      <view class="week-font" v-for="(item, index) in weekArr" :key="index ">{{ weekType + '' + item }}</view>
    </view>
    <!-- 日历 -->
    <swiper class="calendar-data" :current="shrinkType?tranCurrent:tranIndex" circular :duration="tranDuration"
      @change="swiperChange" @animationfinish="swiperEndChange" :style="{height:shrinkType?'60px':'280px'}">
      <swiper-item class="swiper-item swiper-prev-item" v-for="(a, i) in getAllData" :key="i">
        <view class="month-bg" v-if="showMonthBg">{{ getMontBg }}</view>
        <view class="month-days" :class="shrinkType?'item-week':''">
          <view class="week-days" v-for="(b, j) in a" :key="j">
            <view class="day" v-for="(c, k) in b" :key="k">
              <view class="day-info" v-if="isBetween(c.date,startDate,endDate)" @click="clickDay(c)"
                :class="[c.dayClass, getIsSelDay(c)?'is-sel':'', c.dayType!='normal'?'un-month':'']">
                <text class="day-solar">{{ c.day }}</text>
                <template v-if="showLunar || c.sign && c.sign.length > 0">
                  <text :class="'day-tag day-tag-' + dayTagPos" v-if="c.sign && c.sign.length > 0"></text>
                  <text class="day-sign"
                    v-if="showSignTitle && c.sign && c.sign.length > 0">{{ c.sign[0].title }}</text>
                  <text class="day-lunar" v-else>{{c.dayLunar}}</text>
                </template>
              </view>
              <view class="day-info" v-else
                :class="[c.dayClass, getIsSelDay(c)?'is-sel':'', c.dayType!='normal'?'un-month':'']">
                <text class="day-solar is-disabled">{{ c.day }}</text>
                <template v-if="showLunar || c.sign && c.sign.length > 0">
                  <text :class="'day-tag is-disabled day-tag-' + dayTagPos" v-if="c.sign && c.sign.length > 0"></text>
                  <text class="day-sign is-disabled" v-if="c.sign && c.sign.length > 0">{{ c.sign[0].title }}</text>
                  <text class="day-lunar is-disabled" v-else>{{c.dayLunar}}</text>
                </template>
              </view>
            </view>
          </view>
        </view>
      </swiper-item>
    </swiper>
    <!-- 收缩按钮 -->
    <view v-if="showShrink" class="shrink" @click="changeShrink">{{shrinkType ? '展开日历' : '收起日历'}}</view>
  </view>
</template>
<script>
  let {
    calendar
  } = require("./calendar.js");
  /**
   * @property {Boolean} showLunar = [true|false] 是否显示农历，默认false
   * @property {Boolean} showMonthBg = [true|false] 是否显示月份背景，默认true
   * @property {Boolean} showChangeBtn = [true|false] 是否显示上月下月箭头按钮，默认true
   * @property {String} firstDayOfWeek = [monday|sunday] 周几为每周的第一天，默认monday
   *  @value monday 每周从周一开始(默认)
   *  @value sunday 每周从周日开始
   * @property {String} weekType = [''|周|星期] 星期的前缀；如周一周二或星期一星期二，为空则只显示一、二等；不用预设值时可自定义前缀，填的值即为星期前缀；默认周
   *  @value '' 星期显示：只显示一、二等
   *  @value 周 星期显示：周一、周二等(默认)
   *  @value 星期 星期显示：星期一、星期二等
   * @property {Boolean} weekend = [true|false] 周末标红(周六周日日期用红色字体)，默认true
   * @property {Boolean} showShrink = [true|false] 是否显示收缩按钮，可显示一周的日期，默认false
   * @property {String} shrinkState = [week|month] 收缩状态，默认month
   *  @value week 默认打开显示周数据（收起状态）
   *  @value month 默认打开显示月数据（展开状态）
   * @property {Array} signList 标记数组，若当前有多个标记，则显示最后一个，期待格式[{date: '2021-09-10', title: '生日', info: '八月初四张三生日'}]
   * @event {Function()} dayChange 点击日期触发事件
   * @event {Function()} monthChange 切换月份触发事件
   * @event {Function()} shrinkClick 收缩和展开时触发事件
   */
  export default {
    name: 'JsCalendar',
    props: {
      //显示日期
      date: {
        type: String,
        default: ''
      },
      //是否显示农历
      showLunar: {
        type: Boolean,
        default: false
      },
      //是否显示月份背景
      showMonthBg: {
        type: Boolean,
        default: true
      },
      //是否显示上月下月按钮
      showChangeBtn: {
        type: Boolean,
        default: true
      },
      //每周的周几为第一天
      firstDayOfWeek: {
        type: String,
        default: 'monday'
      },
      //每周的周几为第一天
      weekType: {
        type: String,
        default: '周'
      },
      //周末标红
      weekend: {
        type: Boolean,
        default: true
      },
      //是否可收缩，收起来后以周显示
      showShrink: {
        type: Boolean,
        default: false
      },
      //收缩文字
      shrinkText: {
        type: String,
        default: ""
      },
      // 默认打开状态（收起或展开）
      shrinkState: {
        type: String,
        default: 'month'
      },
      //标记
      signList: {
        type: Array,
        default () {
          return []
        }
      },
      startDate: {
        type: String,
        default: ''
      },
      endDate: {
        type: String,
        default: ''
      },
      // 是否显示标题
      showSignTitle: {
        type: Boolean,
        default: true
      },
      //日期指示标签的位置，top-left为左上角、top-right为右上角、top-center为中上角、bottom-left为左下角、bottom-right为右下角、bottom-center为中下角
      dayTagPos: {
        type: String,
        default: 'top-right'
      }
    },
    data() {
      return {
        weekArr: ['一', '二', '三', '四', '五', '六', '日'], //星期数组
        today: {}, //今天日期 -> year, month, day
        selDate: {}, //选中日期信息 -> year, month, day

        allMonthList: [], // 月份数据 -> [[[周],[周]],[月],[月]]
        tranIndex: 1, // 月份轮播所在位置
        allWeekList: [], // 周月份数据 -> [[[周]],[月],[月]]
        tranCurrent: 1, // 周轮播所在位置
        tranDuration: 300, //轮播时间(单位毫秒)

        signArr: this.signList, // 标记列表
        showToday: false, //显示回到今天(非当月才显示)
        shrinkType: false, // 收缩状态，true:收起(显示周)，false展开(显示月)
        deterChange: true, // 防止切换月份过快,
      }
    },
    created() {
      // debugger
      let nd = null;
      if (!this.date) {
        nd = new Date();
      } else {
        nd = new Date(this.date);
      }
      this.setDate(nd);
    },
    watch: {
      date(val) {
        this.setDate(val);
      },
      startDate(val) {
        this.startDate = val;
      },
      endDate(val) {
        this.endDate = val;
      }
    },
    computed: {
      getAllData() { // 切换周或月时，展示的数据不同
        return this.shrinkType ? this.allWeekList : this.allMonthList;
      },
      getMontBg() { // 获取当前月背景
        let monthBg = this.selDate.month;
        return !this.shrinkType ? (monthBg < 10 ? '0' + monthBg : monthBg) : '';
      },
      getIsSelDay() { // 判断是否是选中日期
        return (d) => {
          let {
            year,
            month,
            day
          } = this.selDate;
          return year == d.year && month == d.month && day == d.day
        }
      },
    },
    methods: {
      initDate() { // 初始化日期
        this.selDate = JSON.parse(JSON.stringify(this.today));
        let monthList = this.getMonthData(this.selDate); // 获取当月数据
        let prevMonthList = this.getMonthData(this.getMonthDate(this.selDate, -1)); // 上月数据
        let nextMonthList = this.getMonthData(this.getMonthDate(this.selDate)); // 下月数据
        this.allMonthList = [prevMonthList, monthList, nextMonthList]
        this.tranIndex = 1;
        if (this.shrinkState == "week" && !this.shrinkType) this.changeShrink();
      },
      /**
       * 根据指定日期获取当月的数据
       * @param {Object} date = { year, month, day } 指定的日期
       */
      getMonthData(date) {
        const {
          year,
          month,
          day
        } = date; //指定的日期
        let maxDay = new Date(year, month, 0).getDate(); //当前月最大日期
        let firstWeek = new Date(year + "/" + month + "/1").getDay(); //月份1号的星期数
        if (this.firstDayOfWeek == "monday") firstWeek = firstWeek - 1 < 0 ? 6 : firstWeek - 1;
        let list = [];
        //每月显示42天，6周，每周7天
        for (var i = 0; i < 42; i++) {
          let dayInfo = {}; // 每天的详细信息
          if (i < firstWeek) { //指定月份上月的最后几天
            let {
              year,
              month
            } = this.getMonthDate(date, -1);
            let preMaxDay = new Date(year, month, 0).getDate(); //上月最大日期
            let day = preMaxDay - firstWeek + i + 1;
            dayInfo = this.getDayInfo({
              year,
              month,
              day
            }, 'prev');
          } else if (i > maxDay + firstWeek - 1) { //指定月份下月的前几天
            let {
              year,
              month
            } = this.getMonthDate(date);
            let day = i - maxDay - firstWeek + 1;
            dayInfo = this.getDayInfo({
              year,
              month,
              day
            }, 'next');
          } else {
            let day = i - firstWeek + 1;
            dayInfo = this.getDayInfo({
              year,
              month,
              day
            }, 'normal');
          }
          if (i % 7 == 0) list.push(new Array());
          list[list.length - 1].push(dayInfo);
        }
        return list;
      },
      /**
       * 获取指定日期的详细信息，包括农历节假日等
       * @param {Object} date = { year, month, day } 指定的日期
       * @param {String} dayType = [prev|next|normal] 日期类型，上月|下月|当前月
       */
      getDayInfo(date, dayType) {
        const {
          year,
          month,
          day
        } = date;
        let isToday = false; //是否今天
        if (year == this.today.year && month == this.today.month && day == this.today.day) isToday = true;
        let week = new Date(year + "/" + month + "/" + day).getDay(); //星期数
        let lunar = calendar.solar2lunar(year, month, day); //农历
        let dayLunar = lunar.IDayCn == '初一' ? lunar.IMonthCn + lunar.IDayCn : lunar.IDayCn;
        if (lunar.festival) dayLunar = lunar.festival; // 阳历节日
        else if (lunar.lunarFestival) dayLunar = lunar.lunarFestival; // 农历节日
        else if (lunar.Term) dayLunar = lunar.Term; // 节气
        let holidayArr = ["元旦", "春节", "清明节", "劳动节", "端午节", "中秋节", "国庆节"];
        let isHoliday = false;
        if (holidayArr.indexOf(dayLunar) != -1) isHoliday = true;
        let dayInfo = {
          date: year + "-" + month + "-" + day,
          year,
          month,
          day,
          week,
          lunar, // 农历
          dayLunar, // 显示的农历
          isToday, // 是否是今日
          isHoliday, // 是否是节假日
          dayType, // 日期类型，上月、下月或当前月
          sign: this.getSignByDate(date)
        }
        let dayClass = this.getDayClass(dayInfo);
        dayInfo["dayClass"] = dayClass;
        return dayInfo;
      },
      /**
       * 根据日期详细信息添加对应的class
       * @param {Object} dayInfo 日期详情
       */
      getDayClass(dayInfo) {
        let dClass = "";
        if (dayInfo.isToday) dClass += ' is-today'; // 今天日期
        if (dayInfo.isHoliday) dClass += ' is-holiday'; // 法定假日
        if (this.weekend && (dayInfo.week == 0 || dayInfo.week == 6)) dClass += ' week-end'; // 周末标红
        return dClass;
      },
      /**
       * 根据日期获取日期对应的事件
       * @param {Object} date = { year, month, day } 指定的日期
       */
      getSignByDate(date) {
        const {
          year,
          month,
          day
        } = date;
        let dayDateS = new Date(year + "/" + month + "/" + day + " 00:00:00").getTime();
        let dayDateE = new Date(year + "/" + month + "/" + day + " 23:59:59").getTime();
        let daySign = [];
        this.signArr.map(sign => {
          let signDate = sign.date.replace(/-/g, '/');
          let signTimes = new Date(sign.date).getTime();
          if (signTimes >= dayDateS && signTimes <= dayDateE) daySign.push(sign)
        })
        return daySign;
      },

      /**
       * 获取月份数据
       * @param {String} type=[pre|next]
       */
      getOtherData(type) {
        let nowMont = this.getMonthDate(this.selDate, type == 'prev' ? -1 : 1); // 获取当前月份
        this.selDate = nowMont; // 切换月份后设置选中的日期
        let monthData = this.getMonthData(this.getMonthDate(nowMont, type == 'prev' ? -1 : 1));
        // 获取上月或下月轮播所在位置
        let current = this.getTranIndex().prevNum;
        if (type == "next") current = this.getTranIndex().nextNum;
        this.allMonthList.splice(current, 1, monthData);
        this.judgeShowToday();
        this.returnMonthChange(type);
      },
      /**
       * 获取周数据
       * @param {String} type=[pre|next]
       */
      getOtherWeekData(type) {
        let oldSel = this.selDate; // 原选中的日期
        let newSel = this.getDateByDateAndDay(oldSel, type == 'prev' ? -7 : 7); // 获取7天前或后的日期
        if (oldSel.month != newSel.month) { // 跨月，先设置跨月后的月历
          // 设置月轮播位置
          let current = this.getTranIndex("month").prevNum;
          if (type == "next") current = this.getTranIndex("month").nextNum;
          this.tranIndex = current;
          this.getOtherData(type);
        }
        this.selDate = newSel;
        this.getWeekData(type);
        this.judgeShowToday();
      },

      // 从月历中获取周数据，切换周后获取上周或下周数据
      getWeekData(type) {
        const {
          prevNum: prevIndex,
          nowNum: nowIndex,
          nextNum: nextIndex
        } = this.getTranIndex("month");
        const {
          prevNum: prevCurrent,
          nowNum: nowCurrent,
          nextNum: nextCurrent
        } = this.getTranIndex("week");
        const {
          year: selYear,
          month: selMonth,
          day: selDay
        } = this.selDate;
        let sDate = selYear + "-" + selMonth + "-" + selDay
        let prevMonthList = this.allMonthList[prevIndex];
        let nowMonthList = this.allMonthList[nowIndex];
        let nextMonthList = this.allMonthList[nextIndex];
        for (let i = 0; i < nowMonthList.length; i++) {
          for (let j = 0; j < nowMonthList[i].length; j++) {
            if (sDate == nowMonthList[i][j].date) {
              this.returnDayChange(nowMonthList[i][j]); // 返回选中的日期
              if (type == "next") {
                this.allWeekList.splice(nextCurrent, 1, [nowMonthList[i + 1]]);
                if (i == 5) this.allWeekList.splice(nextCurrent, 1, [nextMonthList[1]]);
              } else {
                this.allWeekList.splice(prevCurrent, 1, [nowMonthList[i - 1]]);
                if (i == 0) {
                  for (let k = prevMonthList.length - 1; k >= 0; k--) {
                    if (prevMonthList[k][6].dayType == "normal") {
                      this.allWeekList.splice(prevCurrent, 1, [prevMonthList[k]]);
                      break;
                    }
                  }
                }
              }
              break;
            }
          }
        }
      },
      // 根据月份数据获取周数据，相当初始化周数据
      getAllWeekData() {
        const {
          prevNum,
          nowNum,
          nextNum
        } = this.getTranIndex("month");
        const {
          year: selYear,
          month: selMonth,
          day: selDay
        } = this.selDate;
        let sDate = selYear + "-" + selMonth + "-" + selDay; // 选中的日期
        let allWeekList = [
          [],
          [],
          []
        ];
        let prevMonthList = this.allMonthList[prevNum];
        let nowMonthList = this.allMonthList[nowNum];
        let nextMonthList = this.allMonthList[nextNum];
        for (let i = 0; i < nowMonthList.length; i++) {
          for (let j = 0; j < nowMonthList[i].length; j++) {
            if (sDate == nowMonthList[i][j].date) {
              allWeekList[0][0] = nowMonthList[i - 1];
              allWeekList[1][0] = nowMonthList[i];
              allWeekList[2][0] = nowMonthList[i + 1];
              if (i == 5) {
                allWeekList[2][0] = nextMonthList[1];
              } else if (i == 0) {
                for (let k = prevMonthList.length - 1; k >= 0; k--) {
                  if (prevMonthList[k][6].dayType == "normal") {
                    allWeekList[0][0] = prevMonthList[k];
                    break;
                  }
                }
              }
              break;
            }
          }
        }
        this.allWeekList = allWeekList;
      },
      // 滑动切换结束
      swiperEndChange() {
        this.tranDuration = 300;
      },
      // 滑动切换月份或周
      swiperChange(e) {
        let current = e.detail.current;
        let oldIndex = this.shrinkType ? this.tranCurrent : this.tranIndex;
        let type = (oldIndex - current == -1 || oldIndex - current == 2) ? 'next' : 'prev';
        if (this.shrinkType) {
          this.tranCurrent = current;
          if (current != oldIndex) this.getOtherWeekData(type);
        } else {
          this.tranIndex = current;
          if (current != oldIndex) this.getOtherData(type);
        }
      },
      // 点击切换月份或周（上月下月切换或上周下周切换）type = [prev|next] 切换类型
      changeMonthOrWeek(type) {
        if (!this.deterChange) return;
        this.deterChange = false;
        setTimeout(_ => {
          this.deterChange = true;
        }, 400); // 防止点击过快
        this.tranDuration = 300;
        let tranType = this.shrinkType ? 'week' : 'month';
        let current = this.getTranIndex(tranType).prevNum;
        if (type == "next") current = this.getTranIndex(tranType).nextNum;
        if (tranType == "week") {
          this.tranCurrent = current;
          this.getOtherWeekData(type);
        } else {
          this.tranIndex = current;
          this.getOtherData(type);
        }
      },
      // 点击收缩按钮，切换显示月份或显示周
      changeShrink() {
        this.shrinkType = !this.shrinkType;
        if (this.tranDuration != 0) this.tranDuration = 0;
        if (this.shrinkType) {
          this.tranCurrent = 1;
          this.getAllWeekData();
        }
        this.returnShrinkChange();
        this.judgeShowToday();
      },
      // 点击回到今天
      goToday() {
        if (this.tranDuration != 0) this.tranDuration = 0;
        let oldDate = JSON.parse(JSON.stringify(this.selDate));
        this.initDate();
        if (this.shrinkType) {
          this.tranCurrent = 1;
          this.getAllWeekData();
          let today = this.today;
          // 判断是否需要触发改变月份事件
          if (oldDate.year != today.year || oldDate.month != today.month) {
            this.returnMonthChange("today");
          } else {
            this.returnDayChange(this.today);
          }
        } else {
          this.returnMonthChange("today"); // 事件
        }
        this.judgeShowToday();
      },
      // 点击日期
      clickDay(dayInfo) {
        let {
          year,
          month,
          day
        } = this.selDate;
        if (day == dayInfo.day && month == dayInfo.month && year == dayInfo.year) return;
        let oldSel = JSON.parse(JSON.stringify(this.selDate));
        this.selDate.day = dayInfo.day;
        if (oldSel.month != dayInfo.month) {
          if (!this.shrinkType) {
            this.changeMonthOrWeek(dayInfo.dayType);
            return;
          } else {
            this.selDate.year = dayInfo.year;
            this.selDate.month = dayInfo.month;
            let current = this.getTranIndex("month").prevNum;
            if (dayInfo.dayType == "next") current = this.getTranIndex("month").nextNum;
            this.tranIndex = current;
            let monthData = this.getMonthData(this.getMonthDate(this.selDate, dayInfo.dayType == 'prev' ? -1 : 1));
            let current2 = this.getTranIndex("month").prevNum;
            if (dayInfo.dayType == "next") current2 = this.getTranIndex("month").nextNum;
            this.allMonthList.splice(current2, 1, monthData); // 设置上月或下月数据
          }
          this.returnMonthChange(dayInfo.dayType);
        } else {
          this.returnDayChange(dayInfo);
        }
      },
      // 判断是否需要显示回到今天（非本月或本周时显示）
      judgeShowToday() {
        const {
          year,
          month,
          day
        } = this.today;
        const {
          year: selYeat,
          month: selMonth,
          day: selDay
        } = this.selDate;
        if (this.shrinkType) { // 显示的周
          let selTimes = new Date(selYeat, selMonth - 1, selDay).getTime(); // 选中日期的时间戳
          let week = new Date(year, month - 1, day).getDay(); // 今天星期
          let firstWD = this.getDateByDateAndDay(this.today, -week + (this.firstDayOfWeek == "monday" ? 1 : 0));
          let lastWD = this.getDateByDateAndDay(this.today, 6 - week + (this.firstDayOfWeek == "monday" ? 1 : 0));
          let firstTimes = new Date(firstWD.year, firstWD.month - 1, firstWD.day).getTime();
          let lastTimes = new Date(lastWD.year, lastWD.month - 1, lastWD.day).getTime();
          if (selTimes > lastTimes || selTimes < firstTimes) this.showToday = true;
          else this.showToday = false;
        } else {
          if (year != selYeat || month != selMonth) this.showToday = true;
          else this.showToday = false;
        }
      },

      // 重新设置标记
      setSignList() {
        this.allMonthList.map(month => {
          month.map(week => {
            week.map(day => {
              day.sign = this.getSignByDate({
                year: day.year,
                month: day.month,
                day: day.day
              })
            })
          })
        })
      },
      /**
       * 添加标记
       * @param {Array} list 需要添加的标记
       */
      addSignList(list) {
        let signArr = this.signArr.concat(list);
        this.signArr = signArr;
        this.setSignList();
      },
      /**
       * 删除标记
       * 根据date和title共同判断是否删除
       * @param {Array} list 需要删除的标记
       */
      deleteSignList(list) {
        let signArr = this.signArr;
        signArr = signArr.filter(s => {
          if (list.find(l => l.date == s.date && l.title == s.title)) return false
          else return true;
        })
        this.signArr = signArr;
        this.setSignList();
      },
      deleteAllSignList() {
        this.signArr = [];
        this.setSignList();
      },
      /**
       * 事件 - 设置返回日期
       * @param {Object} dayInfo 日期详情
       */
      returnDayChange(dayInfo) {
        let {
          year,
          month,
          day
        } = dayInfo;
        let dayDate = year + "-" + (month < 10 ? '0' + month : month) + "-" + (day < 10 ? '0' + day : day)
        let returnData = {
          date: dayDate,
          year: year,
          month: month,
          day: day,
          week: dayInfo.week,
          daySign: dayInfo.sign
        }
        if (this.showLunar) returnData["lunar"] = dayInfo.lunar;
        this.$emit("dayChange", returnData);
      },
      /**
       * 事件 - 设置返回月份
       * @param {String} type 类型
       */
      returnMonthChange(type) {
        let selDate = this.selDate.year + "-" + this.selDate.month + "-" + this.selDate.day;
        let monthList = this.allMonthList.flat().flat(); // 二维转一维
        let dayInfo = monthList.find(day => day.date == selDate);
        this.returnDayChange(dayInfo)
        this.$emit("monthChange", {
          year: dayInfo.year,
          month: dayInfo.month,
          type: type
        });
      },
      /**
       * 事件 - 返回收缩状态
       */
      returnShrinkChange() {
        let type = this.shrinkType ? 'week' : 'month'
        this.$emit("shrinkClick", type);
      },
      /**
       * 获取上一个或下一个轮播位置
       * @param {String} type = [month|week] 轮播类型，月轮播(tranIndex),周轮播(tranCurrent)
       * @returns {Object} {prevNum, nowNum, nextNum}
       */
      getTranIndex(type = 'month') {
        let current = this.tranIndex;
        if (type == "week") current = this.tranCurrent;
        let prevNum = current - 1 < 0 ? 2 : current - 1;
        let nowNum = current;
        let nextNum = current + 1 > 2 ? 0 : current + 1;
        return {
          prevNum,
          nowNum,
          nextNum
        }
      },
      /**
       * 根据日期获取几天后的日期
       * @param {Object} date = {year, month, day} 当前日期
       * @param {Number} day 当前日期的几天前或几天后(负数)
       * @returns {Object} {year, month, day}
       */
      getDateByDateAndDay(date, num) {
        let dTime = new Date(date.year + "/" + date.month + "/" + date.day).getTime() + num * 24 * 60 * 60 * 1000;
        let nd = new Date(dTime);
        return {
          year: nd.getFullYear(),
          month: nd.getMonth() + 1,
          day: nd.getDate()
        }
      },
      /**
       * 获取几个月前或后的日期
       * @param {Object} date = {year, month, day} 当前日期
       * @param {Number} num 当前日期的num月前或后，默认1月后(下月)
       * @returns {Object} {year, month, day}
       */
      getMonthDate(date, num = 1) {
        let nextMonth = date.month + num;
        let diffYear = parseInt(Math.abs(nextMonth) / 12);
        let year = date.year;
        let month = nextMonth;
        if (nextMonth > 12) {
          year = date.year + diffYear;
          month = nextMonth % 12;
        } else if (nextMonth < 1) {
          year = date.year - (diffYear + 1);
          month = nextMonth + 12 * (diffYear + 1);
        }
        let monthMaxDay = new Date(year, month, 0).getDate(); // 月份最大日期
        let day = date.day > monthMaxDay ? monthMaxDay : date.day;
        return {
          year,
          month,
          day
        }
      },
      isBetween(date, startDate, endDate) {
        if (!startDate || !endDate) {
          return true;
        }
        return calendar.dateBetween(date, startDate, endDate);
      },
      setDate(date) {
        let nd = new Date(date);
        this.today = {
          year: nd.getFullYear(),
          month: nd.getMonth() + 1,
          day: nd.getDate()
        }
        if (this.firstDayOfWeek == "sunday") this.weekArr = ['日', '一', '二', '三', '四', '五', '六'];
        this.initDate();
      }
    }
  }
</script>
<style lang="scss">
  .js-calendar {
    position: relative;
    background: #3989FC;

    // 头部
    .header {
      display: flex;
      flex-direction: row;
      justify-content: center;
      position: relative;
      height: 90rpx;
      line-height: 90rpx;

      .head-month {
        padding: 0 40rpx;
        font-size: 42rpx;
        font-weight: 700;
        color: #fff;
      }

      .go-to-today {
        position: absolute;
        right: 0;
        top: 26rpx;
        padding: 8rpx 12rpx 8rpx 22rpx;
        background-color: rgba(255, 184, 0, 1);
        border-radius: 22rpx 0 0 22rpx;
        font-size: 24rpx;
        line-height: 24rpx;
        color: #FFF;
      }

      .head-icon {
        width: 20rpx;
        height: 20rpx;
        margin-top: 36rpx;
      }

      .head-icon::after {
        content: '';
        display: block;
        width: 18rpx;
        height: 18rpx;
        border-top: 2rpx solid #fff;
        border-left: 2rpx solid #fff;
      }

      .head-icon.head-pre-month {
        transform: rotate(-45deg);
      }

      .head-icon.head-next-month {
        transform: rotate(135deg);
      }
    }

    // 星期
    .week-area {
      display: flex;
      flex-direction: row;
      align-items: center;
      border-bottom: 8rpx solid #F2F2F2;
      border-radius: 40rpx 40rpx 0 0;
      padding-top: 32rpx;
      // margin: 0 10rpx;
      background: #fff;

      .week-font {
        flex: 1;
        padding: 24rpx 0;
        text-align: center;
        font-size: 32rpx;
        font-weight: 700;
        color: #333;
      }
    }

    // 日历
    .calendar-data {
      background: #fff;
      // transition: all 300ms;

      .swiper-item {
        position: relative;
        display: -webkit-box;

        .month-bg {
          position: absolute;
          font-size: 240px;
          font-weight: bold;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -55%);
          color: #EEE;
          opacity: 0.4;
          z-index: -1;
          word-break: initial;
        }

        .month-days {
          width: 100%;
          margin-top: 16rpx;
          position: relative;
          // transition: all 300ms;

          .week-days {
            display: flex;
            flex-direction: row;

            .day {
              flex: 1;
              width: 14.28%;
              text-align: center;
              height: 88rpx;
              color: #333;
              padding: 0 6rpx;
              box-sizing: border-box;

              .day-info {
                height: 100%;
                display: flex;
                flex-direction: column;
                justify-content: flex-start;
                align-items: center;
                position: relative;

                .day-solar {
                  display: block;
                  font-size: 34rpx;
                  line-height: 34rpx;
                  margin-top: 18rpx;
                }

                .day-lunar,
                .day-sign {
                  // color: #909399;
                  font-size: 24rpx;
                  line-height: 24rpx;
                  transform: scale(0.8);
                  white-space: nowrap;
                }

                .is-disabled {
                  color: #c0c0c0;
                }

                .day-sign {
                  color: #F75858 !important;
                }

                .day-tag {
                  content: "";
                  position: absolute;
                  // top: 8rpx;
                  // right: 8rpx;
                  width: 10rpx;
                  height: 10rpx;
                  border-radius: 50%;
                  display: inline-block;
                  background-color: #F75858;
                }

                .day-tag-top-left {
                  top: 8rpx;
                  left: 8rpx;
                }

                .day-tag-top-right {
                  top: 8rpx;
                  right: 8rpx;
                }

                .day-tag-top-center {
                  top: 0rpx;
                }

                .day-tag-bottom-left {
                  bottom: 8rpx;
                  left: 8rpx;
                }

                .day-tag-bottom-right {
                  bottom: 8rpx;
                  right: 8rpx;
                }

                .day-tag-bottom-center {
                  bottom: -3rpx;
                }
              }

              // 非当月日期
              .day-info.un-month {
                opacity: 0.25;
                transition: all 300ms;
              }

              // 今天日期
              .is-today .day-solar,
              .is-today .day-sign,
              .is-today .day-lunar {
                color: #409eff;
              }

              // 周末
              .week-end .day-solar {
                color: #FF9595;
              }

              // 法定假日
              .is-holiday .day-solar,
              .is-holiday .day-sign,
              .is-holiday .day-lunar {
                // color: #F75858 !important;
              }

              // 当前选中的日期
              .is-sel {
                border-radius: 8rpx;
                background: #3989FC;

                .day-solar {
                  color: #fff;
                }

                .day-lunar {
                  color: #E1E1E1;
                }
              }
            }
          }

          .week-days.week-hide {
            display: none;
          }
        }

        .item-week {
          .un-month {
            opacity: 1 !important;
          }
        }
      }
    }

    // 收缩按钮
    .shrink {
      position: absolute;
      right: 32rpx;
      bottom: -24rpx;
      z-index: 99;
      display: inline-block;
      border-radius: 56rpx;
      padding: 0 32rpx;
      margin-bottom: -56rpx;
      line-height: 56rpx;
      font-size: 24rpx;
      color: #3989FC;
      background: #DEEBFE;
    }
  }
</style>